#include <QUdpSocket>
#include <QSqlQuery>
#include <QSqlError>
#include <QtSql>
#include <QHostAddress>
#include "p2pserver.h"
#include "macro.h"
#include "dialog.h"
#include "p2ppeer.h"

P2PServer::P2PServer(QObject *parent) :
    QObject(parent)
{
    udpSocketOfP2PServer= ((WeatherStation*)parent)->udpSocket;
}

/*
增加一个视频源，即新建一棵树，初始化根节点。
当有新的摄像头把数据传送给server时，调用该函数。
参数说明：
sourceIP:视频源的IP。可以是服务器的IP，也可以是连接摄像头的节点的IP
sourcePort:视频源的端口。同上。
nodeMacAddr:摄像头所连节点（PC也可以视为一个节点）
sensorID:传感器类型。主要是11和12，分别表示Video和VideoUSB
 */
bool    P2PServer::addVideoSource(QString sourceIP, quint16 sourcePort, qint64 nodeMacAddr, quint8 sensorID)
{
    QSqlQuery sqlQueryTemp(QSqlDatabase::database(DB_CON_NAME_WEATHERSTATION));

    if(!sqlQueryTemp.exec("insert into topologytree(IPv4,Port,NodeMacAddr,SensorID,Degree,Delay,Priority,Level) "
           " values('"+sourceIP+"',"+QString::number(sourcePort)+","+QString::number(nodeMacAddr)+
           ","+QString::number(sensorID)+",0,0,1,1)" ))
    {
        qDebug()<<"fail in addVideoSource"<<sqlQueryTemp.lastError();
        return false;
    }
    else
        return true;
}

/*
 1.查询视频资源的命令。
 这个命令看起来大致是这样的: “Video  List  Sources”
 2.请求观看实时视频的命令。
 这个命令看起来大致是这样的: "VideoHik 9003 RealPlayBegin"或"VideoMJPEG 9003 RealPlayStop"

*/
QByteArray P2PServer::handleComingRequest(QHostAddress clientIPReq, quint16 clientPortReq, const QByteArray &datagram)
{
    QList <QByteArray> tempList;
    QByteArray reply;
    tempList=datagram.simplified().split(' ');
    if(datagram.simplified().toUpper()=="VIDEO LIST SOURCES")
    {
        return "Should not enter this part in P2PServer::handleComingRequest";
        //不应该加入这部分内容
    }
     else if(tempList.size()!=3)
    {
        reply="ERROR:parameters should be 3,and seperated by space";
        return reply;
        //udpSocketOfP2PServer->writeDatagram(reply,clientIPReq,clientPortReq);
    }
    else if(datagram.endsWith("RealPlayBegin")||datagram.endsWith("RealPlayBegin;"))
    {
        reply=addOnePeer(clientIPReq,clientPortReq,datagram);
    }

    else if(datagram.endsWith("RealPlayStop")||datagram.endsWith("RealPlayStop;")) //结束视频直播
    {
        reply=removeOnePeer(clientIPReq,clientPortReq,datagram);
    }
    else
        reply="ERROR:Param errror;";

    return reply;
}

/*
 增加一个peer。更新拓扑树，并最后调用shareWithPeer()函数。该函数通知父节点，给新加入
 拓扑树的他的子节点提供视频数据。
 */
QByteArray  P2PServer::addOnePeer(QHostAddress peerIP, quint16 peerPort, const QByteArray &datagram)
{
    bool ok;
    QString  IPv4;
    qint64  macAddress;
    QList <QByteArray> tempList;
    QByteArray  reply;
    quint8  sensorID(0);
    tempList=datagram.simplified().split(' ');
    macAddress=tempList.at(1).toULongLong(&ok);
    if(tempList.at(0)=="VideoHik")
        sensorID=11;
    else if(tempList.at(0)=="VideoMJPEG")
        sensorID=12;
    else if(tempList.at(0)=="Video264")
        sensorID=13;    

    //检查该peer是否已经加入了拓扑树
    if( ((WeatherStation*)parent())->p2PPeer->addrChildSocketMap.contains(peerIP.toString()+":"+QString::number(peerPort),macAddress))
    {
        reply="WARNING:This node already included in addrChildSocketMap now. If you think there's something wrong with your"
              "parent and want to change a parent,please stop realplay first";
        return reply;
    }
    QSqlQuery sqlQueryTemp(QSqlDatabase::database(DB_CON_NAME_WEATHERSTATION));

    if(sqlQueryTemp.exec("select IPv4 from TopologyTree where IPV4='"+peerIP.toString()+"' "
             " and Port="+QString::number(peerPort,10)+"  and NodeMacAddr="+QString::number(macAddress)+
         " and SensorID="+QString::number(sensorID)))
    {
        if(sqlQueryTemp.next())
            IPv4=sqlQueryTemp.value(0).toString();
    }
    else
        return "ERROR:fail in addOnePeer()"+sqlQueryTemp.lastError().text().toUtf8();

    //该socket已经在拓扑树中,则不再处理。如果某个用户希望重新选择父节点，则先发送停止播放命令，再发送请求播放
    if(!IPv4.isEmpty())
        reply="WARNING:This node already included in TopologyTree now. If you think there's something wrong with your"
              "parent and want to change a parent,please stop realplay first";

    else  //该socket不在该拓扑树中,则加入一行
    {
        if(!sqlQueryTemp.exec("select IPv4,Port,Degree,LChildIP,Level from TopologyTree where NodeMacAddr="+QString::number(macAddress)+
                                      " and  SensorID="+QString::number(sensorID)+" order by Level asc limit 1") )
            return "ERROR:fail in addOnePeer()"+sqlQueryTemp.lastError().text().toUtf8();
        else
        {
            QString parentIPv4,lChildIP;
            quint16 parentPort(0);  quint8 degree(0),level(0);
            if(sqlQueryTemp.next())
            {
                parentIPv4=sqlQueryTemp.value(0).toString();
                parentPort=sqlQueryTemp.value(1).toUInt(&ok);
                degree=sqlQueryTemp.value(2).toUInt(&ok);
                lChildIP=sqlQueryTemp.value(3).toString();
                level=sqlQueryTemp.value(4).toUInt(&ok);
            }
            else
                return tr("ERROR:找不到合适的视频源").toUtf8();
            if(lChildIP.isEmpty())  //更新父节点信息。如果左孩子是空，则添加到左孩子
            {
                if(!sqlQueryTemp.exec("update topologytree set LChildIP='"+peerIP.toString()+"',"+
                    "LChildPort="+QString::number(peerPort)+",Degree="+QString::number(degree+1)+
                    "  where IPv4='"+parentIPv4+"'  and Port="+QString::number(parentPort) ) )
                    return "ERROR:fail to update topolyTree in addOnePeer()"+sqlQueryTemp.lastError().text().toUtf8();
            }
            else  //更新父节点信息。左孩子不空，则添加到右孩子
            {
                if(!sqlQueryTemp.exec("update topologytree set RChildIP='"+peerIP.toString()+"',"+
                    "RChildPort="+QString::number(peerPort)+",Degree="+QString::number(degree+1)+
                    "  where IPv4='"+IPv4+"'  and Port="+QString::number(parentPort) ) )
                    return "ERROR:fail to update topolyTree in addOnePeer()"+sqlQueryTemp.lastError().text().toUtf8();
            }

            //添加一行。记录新加入的节点的信息
            if(!sqlQueryTemp.exec("insert into  topologytree(IPv4,Port,NodeMacAddr,SensorID,ParentIP,ParentPort,"
                  "Degree,Level)  values('"+peerIP.toString()+"',"+QString::number(peerPort)+
                  ","+QString::number(macAddress)+","+QString::number(sensorID)+",'"+IPv4+"',"+
                  QString::number(parentPort)+",0,"+QString::number(level+1)+");"))  //插入新的一行，表示新加入的客户端
                return "ERROR:fail to insert topolyTree in addOnePeer()"+sqlQueryTemp.lastError().text().toUtf8();

            ((WeatherStation*)parent())->shareWithPeer(QHostAddress(parentIPv4),parentPort,peerIP,peerPort,macAddress);

            ((WeatherStation*)parent())->dbo->insertLog(peerIP.toString()+":"+
                  QString::number(peerPort,10)+"---This socket begin receiving data from nodes whose NodeMacAddr is"
                   +QString::number(macAddress)+" and sensorID is "+QString::number(sensorID));

            reply="INFO:insert toplogytree success";
        }
    }

    return reply;
}

/*
 移去一个perr。更新拓扑树，并通知父节点，停止给子节点提供数据
 */
QByteArray P2PServer::removeOnePeer(QHostAddress peerIP, quint16 peerPort, const QByteArray &datagram)

{
    bool ok;
    qint64  macAddress;
    QList <QByteArray> tempList;
    QByteArray  reply;
    quint8  sensorID(0);
    tempList=datagram.simplified().split(' ');
    macAddress=tempList.at(1).toULongLong(&ok,10);
        qDebug()<<macAddress;
    if(tempList.at(0)=="VideoHik")
        sensorID=11;
    else if(tempList.at(0)=="VideoMJPEG")
        sensorID=12;
    else if(tempList.at(0)=="Video264")
        sensorID=13;

    ((WeatherStation*)parent())->p2PPeer->addrChildSocketMap.remove(peerIP.toString()+
                          ":"+QString::number(peerPort),macAddress);
    QSqlQuery sqlQueryTemp(QSqlDatabase::database(DB_CON_NAME_WEATHERSTATION));

    if(sqlQueryTemp.exec("delete from TopologyTree where IPV4='"+peerIP.toString()+"' "
                                 " and Port="+QString::number(peerPort,10)+"  and NodeMacAddr="+QString::number(macAddress)+
         " and SensorID="+QString::number(sensorID)))
    {  
        ((WeatherStation*)parent())->dbo->insertLog(peerIP.toString()+":"+
              QString::number(peerPort,10)+"---"
               "This socket stoped receiving data from nodes whose NodeMacAddr is"
               +QString::number(macAddress)+" and sensorID is "+QString::number(sensorID));
        return "INFO:remove peer succeed";
    }
    else
       return "ERROR:fail in removeOnePeer()"+sqlQueryTemp.lastError().text().toUtf8();

    return reply;
}

void  P2PServer::doWithDeadSocket(QString deadIP, QString deadPort)
{
    QSqlQuery sqlQueryTemp(QSqlDatabase::database(DB_CON_NAME_WEATHERSTATION));

    if(sqlQueryTemp.exec("delete from TopologyTree where IPV4='"+deadIP+"' "
         " and Port="+deadPort))
    {
        if(sqlQueryTemp.exec("update TopologyTree set LChildIP='',LChildPort=0 "
              " where LChildIP='"+deadIP+"'  and LChildPort="+deadPort))
        {
            if(sqlQueryTemp.exec("update TopologyTree set RChildIP='',RChildPort=0 "
                  " where RChildIP='"+deadIP+"'  and RChildPort="+deadPort))
            {
                ((WeatherStation*)parent())->dbo->insertLog(deadIP+":"+deadPort+"---"
                       "Application using this socket has gone away");
                 qDebug()<<"INFO:remove one socket from topologytree succeed";
             }
        }

    }
    else
        qDebug()<<"INFO:fail to remove one socket from topologytree"<<sqlQueryTemp.lastError();
}

P2PServer::~P2PServer()
{
    QSqlQuery sqlQueryTemp(QSqlDatabase::database(DB_CON_NAME_WEATHERSTATION));

    sqlQueryTemp.exec("truncate topologytree;");
}
